package main

import (
	"bufio"
	"context"
	"io"
	"math"
	"strconv"

	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/google/uuid"

	"spider.com/day/dbinfo"
	"spider.com/day/settings"
)

func sendReq(uuid string, client *http.Client, days string, code string, proxy string) (*settings.DayResponse, error) {

	client.Timeout = 5 * time.Second
	req, err := http.NewRequest("GET", fmt.Sprintf(settings.StockDayLineUrl, code, days), nil)
	if err != nil {
		return nil, err
	}
	// if reqID, ok := ctx.Value("uuidKey").(string); ok {
	//     fmt.Println("Generated UUID:", reqID)
	// } else {
	//     fmt.Println("UUID not found in context")
	// }

	req.Header.Set("User-Agent", settings.Headers[rand.Intn(len(settings.Headers))])
	req.Header.Set("Cookie", settings.Cookies[0])

	if proxy != "" {
		// proxyURL, err := url.Parse("http://127.0.0.1:8080")
		proxyURL, err := url.Parse(proxy)
		if err != nil {
			log.Println(uuid, "Error proxy")
			return nil, err
		}
		transport := &http.Transport{
			Proxy: http.ProxyURL(proxyURL),
		}
		client.Transport = transport
	}

	res, err := client.Do(req)
	if err != nil {
		log.Println(uuid, "Error request")
		return nil, err
	}
	defer res.Body.Close()

	body, err := io.ReadAll(res.Body)
	if err != nil {
		log.Println(uuid, "Error IO ReadAll")
		return nil, err
	}

	if res.StatusCode != 200 {
		return nil, err
	}
	var response settings.DayResponse
	// err = json.NewDecoder(res.Body).Decode(&response)
	err = json.Unmarshal([]byte(string(body)), &response)
	if err != nil {
		log.Println(uuid, "Error Unmarshal")
		return nil, err
	}

	return &response, nil
}

func parseKlines(klines []string) []settings.DayKline {
	ret := make([]settings.DayKline, len(klines))
	for i, line := range klines {
		splitLine := strings.Split(line, ",")

		// 解析日期字符串
		t, err := time.Parse("2006-01-02", splitLine[0])
		if err != nil {
			log.Println("Error parsing date:", err, splitLine[0])
		}

		ret[i] = settings.DayKline{
			Date:       splitLine[0],
			TimeStamp:  t.Unix(),
			StartPrice: splitLine[1],
			EndPrice:   splitLine[2],
			HighPrice:  splitLine[3],
			LowPrice:   splitLine[4],
			AllHands:   splitLine[5],
			AllMoney:   splitLine[6],
			Other1:     splitLine[7],
			Other2:     splitLine[8],
			Other3:     splitLine[9],
			Other4:     splitLine[10],
			Other5:     splitLine[11],
			Other6:     splitLine[12],
			Other7:     splitLine[13],
		}
	}
	return ret
}

func RoundToN(value float64, n int) float64 {
	pow := math.Pow(10, float64(n))
	return math.Round(value*pow) / pow
}

func parseKlinesPg(klines []string, klineChan chan []*settings.DayKlinePg, symbol string) (r []*settings.DayKlinePg) {
	ks := []*settings.DayKlinePg{}
	dates := []string{}
	end := float64(0)
	for _, line := range klines {
		splitLine := strings.Split(line, ",")

		// 解析日期字符串
		t, err := time.Parse("2006-01-02", splitLine[0])
		if err != nil {
			log.Println("Error parsing date:", err, splitLine[0], symbol)
		}

		StartPrice, _ := strconv.ParseFloat(splitLine[1], 64)
		EndPrice, _ := strconv.ParseFloat(splitLine[2], 64)
		HighPrice, _ := strconv.ParseFloat(splitLine[3], 64)
		LowPrice, _ := strconv.ParseFloat(splitLine[4], 64)
		AllHands, _ := strconv.ParseInt(splitLine[5], 10, 64)
		AllMoney, _ := strconv.ParseFloat(splitLine[6], 64)
		Other1, _ := strconv.ParseFloat(splitLine[7], 64)
		Other2, _ := strconv.ParseFloat(splitLine[8], 64)
		Other3, _ := strconv.ParseFloat(splitLine[9], 64)
		// Other4, _ := strconv.ParseFloat(splitLine[10], 64)
		// Other5, _ := strconv.ParseFloat(splitLine[11], 64)
		// Other6, _ := strconv.ParseFloat(splitLine[12], 64)
		// Other7, _ := strconv.ParseFloat(splitLine[13], 64)
		top := 0
		if RoundToN(end*1.1, 2) == EndPrice {
			top = 1
		}
		if RoundToN(end*0.9, 2) == EndPrice {
			top = -1
		}
		dates = append(dates, splitLine[0])
		ks = append(ks, &settings.DayKlinePg{
			Symbol:     symbol,
			Date:       splitLine[0],
			TimeStamp:  t.Unix(),
			StartPrice: StartPrice,
			EndPrice:   EndPrice,
			HighPrice:  HighPrice,
			LowPrice:   LowPrice,
			Top:        top,
			AllHands:   AllHands,
			AllMoney:   AllMoney,
			Other1:     Other1,
			Other2:     Other2,
			Other3:     Other3,
			// Other4:     Other4,
			// Other5:     Other5,
			// Other6:     Other6,
			// Other7:     Other7,
		})
		end = EndPrice

	}

	return ks

}

// func getDayKline(ctx context.Context, ipChan chan string, stockChan chan settings.Stock, klineChan chan *settings.DayStock) {
// 	client := &http.Client{}
// 	days := "250"
// 	for {
// 		ip, ok := <-ipChan
// 		if !ok {
// 			break
// 		}
// 		stock, ok := <-stockChan
// 		if !ok {
// 			break
// 		}
// 		codePrefix := "0."
// 		if stock.Prefix == "SZ" {
// 			codePrefix = "1."
// 		}

// 		newUUID := uuid.New().String()

// 		// ctx = context.WithValue(ctx, "uuidKey", newUUID)

// 		resp, err := sendReq(newUUID, client, days, codePrefix+stock.Code, ip)
// 		if err != nil {
// 			log.Println(newUUID, ip, stock, "ipChan", len(ipChan), "stockChan", len(stockChan), "klineChan", len(klineChan), "Error request", err)
// 			ipChan <- ip
// 			stockChan <- stock
// 			continue
// 		}
// 		if resp == nil {
// 			log.Println(newUUID, ip, stock, "ipChan", len(ipChan), "stockChan", len(stockChan), "klineChan", len(klineChan), "Nil stock")
// 			ipChan <- ip
// 			stockChan <- stock
// 			continue
// 		}
// 		log.Println(newUUID, ip, stock, "ipChan", len(ipChan), "stockChan", len(stockChan), "klineChan", len(klineChan))
// 		parseKlinesPg(resp.Data.Klines, klineChan, stock.Symbol)
// 		ipChan <- ip

// 	}
// }

func getDayKlinePg(ctx context.Context, ipChan chan string, stockChan chan settings.StockPg, klineChan chan []*settings.DayKlinePg) {
	client := &http.Client{}
	days := "200"
	for {
		ip, ok := <-ipChan
		if !ok {
			break
		}
		stock, ok := <-stockChan
		if !ok {
			break
		}
		codePrefix := "1."
		if stock.Prefix == "SZ" {
			codePrefix = "0."
		}
		if stock.Type == 5{
			codePrefix = "0."
		}

		newUUID := uuid.New().String()

		// ctx = context.WithValue(ctx, "uuidKey", newUUID)

		resp, err := sendReq(newUUID, client, days, codePrefix+stock.Code, ip)
		if err != nil {
			log.Println(newUUID, ip, stock, "ipChan", len(ipChan), "stockChan", len(stockChan), "klineChan", len(klineChan), "Error request", err)
			// ipChan <- ip
			stockChan <- stock
			continue
		}
		if resp == nil {
			log.Println(newUUID, ip, stock, "ipChan", len(ipChan), "stockChan", len(stockChan), "klineChan", len(klineChan), "Nil stock")
			// ipChan <- ip
			stockChan <- stock
			continue
		}
		klines := resp.Data.Klines
		if len(klines) > 300 {
			log.Println(newUUID, ip, stock, "ipChan", len(ipChan), "stockChan", len(stockChan), "klineChan", len(klineChan), "to long", resp.Data.Klines[len(klines)-1][20])
			ipChan <- ip
			stockChan <- stock
			continue
		}
		if len(klines) == 0 {
			log.Println(newUUID, ip, stock, "ipChan", len(ipChan), "stockChan", len(stockChan), "klineChan", len(klineChan), "to short")
			ipChan <- ip
			stockChan <- stock
			continue
		}

		splitLine := strings.Split(klines[0], ",")
		t, err := time.Parse("2006-01-02", splitLine[0])
		if err != nil {
			log.Println(newUUID, ip, stock, "ipChan", len(ipChan), "stockChan", len(stockChan), "klineChan", len(klineChan), "parse date error")
			ipChan <- ip
			stockChan <- stock
			continue
		}
		if t.Unix() < 0 {
			log.Println(newUUID, ip, stock, "ipChan", len(ipChan), "stockChan", len(stockChan), "klineChan", len(klineChan), "time date error", t.Unix())
			ipChan <- ip
			stockChan <- stock
			continue
		}

		log.Println(newUUID, ip, stock, "ipChan", len(ipChan), "stockChan", len(stockChan), "klineChan", len(klineChan))
		ks := parseKlinesPg(resp.Data.Klines, klineChan, stock.Symbol)
		ipChan <- ip
		klineChan <- ks
	}

}

func putIp(filename string, ipChan chan<- string) {
	file, err := os.Open(filename)
	if err != nil {
		log.Println("Error opening file:", err)
		close(ipChan) // 确保在出错时关闭通道
		return
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		trimmedLine := strings.TrimSpace(scanner.Text()) // 去除前后空白字符
		// log.Println(trimmedLine)
		ipChan <- "http://" + trimmedLine
	}

	if err := scanner.Err(); err != nil {
		log.Println("Error reading file:", err)
	}
}

func main() {
	// dbinfo.ConnectToMongoDB() // 连接到 MongoDBc
	dbinfo.ConnectToPG()
	// 创建一个上下文对象
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	ipChan := make(chan string, 1000)
	filename := "../ip.txt"
	go putIp(filename, ipChan)
	defer close(ipChan)

	// stockChan := make(chan settings.Stock, 10000)
	// go dbinfo.PutStockFromMongo(ctx, stockChan, dbinfo.CollectionStock)

	// klineChan := make(chan *settings.DayStock, 10000)
	// for i := 0; i < 10; i++ {
	// 	go getDayKline(ctx, ipChan, stockChan, klineChan)
	// }

	stockChan := make(chan settings.StockPg, 10000)
	go dbinfo.PutStockFromPG(ctx, stockChan)

	klineChan := make(chan []*settings.DayKlinePg, 100000)
	for i := 0; i < 100; i++ {
		go getDayKlinePg(ctx, ipChan, stockChan, klineChan)
	}

	defer close(stockChan)
	defer close(klineChan)

	// dbinfo.SaveToMongo(klineChan, dbinfo.CollectionDayLine)
	for i := 0; i < 100; i++ {
		dbinfo.DaySaveToPG(klineChan)
	}
}
